extends CharacterBody2D


const SPEED = 150.0
const JUMP_VELOCITY = -300.0
const FLOOR_ACCELERATION = SPEED/0.2
const AIR_ACCELERATION = SPEED/0.02
const WALL_JUMP_VELOCITY := Vector2(600.0,-300.0)

enum State{
	IDLE,
	RUNNING,
	JUMP,
	FALL,
	LANDING,
	WALL_SLIDING,
	WALL_JUMP
}

const GROUND_STATE = [State.IDLE,State.RUNNING,State.LANDING]


var default_gravity = ProjectSettings.get("physics/2d/default_gravity")
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var graphics: Node2D = $Graphics
@onready var coyote_timer: Timer = $CoyoteTimer
@onready var jump_request_timer: Timer = $JumpRequestTimer
@onready var handeChecker: RayCast2D = $Graphics/HandeChecker
@onready var footChecker: RayCast2D = $Graphics/FootChecker

func _unhandled_input(event: InputEvent) -> void:
	if Input.is_action_just_pressed("jump"):
		jump_request_timer.start()
	if Input.is_action_just_released("jump"):
		jump_request_timer.stop()
		if velocity.y<JUMP_VELOCITY/2:
			velocity.y = JUMP_VELOCITY/2
		
func get_next_state(cstate: State)-> State:
	
	var can_jump = coyote_timer.time_left>0 or is_on_floor()
	var should_jump =can_jump and jump_request_timer.time_left>0
	if should_jump:
		return State.JUMP
	
	var direction = Input.get_axis("move_left","move_right")
	var is_still = is_on_floor() and is_zero_approx(direction) and is_zero_approx(velocity.x)
	match cstate:
		State.IDLE:
			if not is_on_floor():
				return State.FALL
			if not is_still:
				return State.RUNNING
		State.RUNNING:
			if not is_on_floor():
				return State.FALL
			if is_still:
				return State.IDLE
		State.JUMP:
			if velocity.y>0:
				return State.FALL
		State.FALL:
			if is_on_floor():
				return State.LANDING if is_still else State.RUNNING
			if is_on_wall() and handeChecker.is_colliding() and footChecker.is_colliding():
				return State.WALL_SLIDING
		State.LANDING:
			if not is_still:
				return State.RUNNING
			if not animation_player.is_playing():
				return State.IDLE
		State.WALL_SLIDING:
			if not is_on_wall():
				return State.FALL
			if is_on_floor():
				return State.IDLE
			if jump_request_timer.time_left>0:
				return State.WALL_JUMP
		State.WALL_JUMP:
			if velocity.y>0:
				return State.FALL
	return cstate
	
func transition_state(from:State,to:State)->void:
	if from not in GROUND_STATE and to in GROUND_STATE:
		coyote_timer.stop()
	
	match to:
		State.IDLE:
			animation_player.play("idel")
		State.RUNNING:
			animation_player.play("runing")
		State.JUMP:
			animation_player.play("jump")
			velocity.y = JUMP_VELOCITY
			coyote_timer.stop()
			jump_request_timer.stop()
		State.FALL:
			animation_player.play("fail")
			if from in GROUND_STATE:
				coyote_timer.start()
		State.LANDING:
			animation_player.play("landing")
		State.WALL_SLIDING:
			animation_player.play("wall_sliding")
		State.WALL_JUMP:
			animation_player.play("jump")
			velocity = WALL_JUMP_VELOCITY
			velocity.x *= get_wall_normal().x
			jump_request_timer.stop()
			
				
func tick_physics(state:State,delta: float)->void:
	match state:
		State.IDLE:
			move(default_gravity,delta)
		State.RUNNING:
			move(default_gravity,delta)
		State.JUMP:
			move(default_gravity,delta)
		State.FALL:
			move(default_gravity,delta)
		State.LANDING:
			stand(delta)
		State.WALL_SLIDING:
			move(default_gravity/10,delta)
		State.WALL_JUMP:
			move(default_gravity,delta)

func move(default_gravity:float,delta:float)->void:
	if not is_on_floor():
		velocity.y += default_gravity * delta;
	var direction = Input.get_axis("move_left","move_right")
	var acceleration = FLOOR_ACCELERATION if is_on_floor() else AIR_ACCELERATION
	velocity.x = move_toward(velocity.x,direction * SPEED,delta * acceleration)
	if not is_zero_approx(direction):
		graphics.scale.x = -1 if direction < 0 else 1
	move_and_slide()
	
func stand(delta:float)->void:
	if not is_on_floor():
		velocity.y+= default_gravity* delta;
	move_and_slide()
